Skip to content

Conversation

@AWhetter
Copy link
Contributor

Feature or Bugfix

  • Feature

Purpose

This pull request adds a py:type directive for documenting type aliases (https://typing.readthedocs.io/en/latest/spec/aliases.html#type-aliases), and a py:type role for linking to them.

.. py:type:: Alias1
   :value: list[str | int]

   This is my type alias.

:py:type:`Alias1` is my type alias.

Detail

  • I've chosen to make a new directive for documenting type aliases, rather than add a new option to the data or attribute directives, because not all of the options on the data and attribute directives apply to a type alias (eg. type). In addition, type aliases seem to be classified differently from variable assignments by Python. Python documentation treats type aliases almost like first class citizens.

    • Documenting with a new directive means that if a user is currently using type in their code, but they're documenting it using the workaround of documenting it as an assignment (eg. .. py:data:: Url\n :type: TypeAlias\n :value: str), the user will likely want to update their documentation to use this new directive. I'm assuming that the number of users using type in their code is quite low, and therefore this trade off seemed worth it.
  • I've chosen to name the directive and role "type" because that's also the keyword used to define a type alias in Python. I considered using "typealias", "alias", or some thing similar, but it seemed safer to choose the same word used in Python syntax because CPython may change the usage of the type keyword in the future to define more than just type aliases.

    • This overlaps with the :type: field, which potentially makes understanding rST syntax more confusing for users who are learning rST for the first time. But this trade-off seemed worth it.
  • I've chosen to have users specify the type being aliased in a :value: option, rather than in the signature (eg. .. py:type:: MyAlias = int), because this is consistent with how the data and attribute directives work. So I think it makes the syntax more intuitive for users.

  • I've chosen to make specifying a value of the alias optional because there could be cases where a user wants to declare the alias, but not make the type that is aliased public. For example:

    type MyAlias = str
    
    def generate() -> MyAlias:
        ...
    def accept(arg: MyAlias) -> None:
       ...
    
    # To be used like the following:
    accept(generate())

    In the above example, a user may want to allow users to pass this type between parts of the API, without letting the user do anything else with instance of MyAlias, by not documenting what MyAlias is aliased from. So the user would document this as follows:

    .. py:type:: MyAlias
    
    .. py:function:: generate() -> MyAlias
    
    .. py:function:: accept(arg: MyAlias) -> None

    Users can currently do something similar with classes, where they might define a class but not document any of the attributes on it.

    .. py:class:: MyClass
    
    .. py:function:: generate() -> MyClass
    
    .. py:function:: accept(arg: MyClass) -> None
  • I've chosen to name the option "value" because this is consistent with the data and attribute directives. In addition, this is the terminology used in typing.TypeAliasType and in the ast node.

    >>> import ast
    >>> class A:
    ...     type MyAlias = int
    ...
    >>> type(A.MyAlias)
    <class 'typing.TypeAliasType'>
    >>> A.MyAlias.__value__  # Note the attribute name "__value__"
    <class 'int'>
    >>> m = ast.parse("type PuzzleInput = list[tuple[list[str], int]]")
    >>> m.body[0].value  # Note the attribute name "value"
    <ast.Subscript object at 0x7dd5ba91afd0>

Relates

@picnixz
Copy link
Member

picnixz commented Feb 19, 2024

I don't mind the use of :py:type: so I'll review it this w-e. Do you plan having an autodoc thing for this one? (either we wait for 3.12 to become the minimal version since the AST parser will be different, or we implement something to support X: TypeAlias = ...)

@AWhetter
Copy link
Contributor Author

AWhetter commented Feb 22, 2024

I wasn't planning on implementing it in autodoc myself because my use case doesn't require it. But I can implement it if preferred. Either way I figured it belonged in a separate pull request?

@picnixz
Copy link
Member

picnixz commented Feb 22, 2024

Yes it would. I think we can delay the autodoc implementation for now since I want to hear from other maintainers the direction we'll take for improving autodoc.

@chrisjsewell
Copy link
Member

since I want to hear from other maintainers the direction we'll take for improving autodoc.

@picnixz, if you have time, it would be great if you could open a discussion, similarish to #12152 😄;
trying to outline the problem, the current implementation and your understanding of its design choices, then an outline of potential solutions

Obviously from I have some thoughts on this I could add (e.g. https://github.com/sphinx-extensions2/sphinx-autodoc2?tab=readme-ov-file#design-and-comparison-to-sphinx-autoapi), but would be good to baseline the discussion first

@picnixz
Copy link
Member

picnixz commented Apr 3, 2024

trying to outline the problem, the current implementation and your understanding of its design choices, then an outline of potential solutions

I'll try to find time for that tomorrow. And actually, tomorrow I'll do more a review thing rather than coding (#12219 is still a draft but it could be reviewed I think? I tried to have as much coverage as possible since this is something you want to use in tests).

I'll also review this PR since I said that I would do it last month...

Copy link
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments (and sorry for the dealy, and thank your for your contribution)

Comment on lines 319 to 335
.. rst:directive:: .. py:type:: name
Describes a :ref:`type alias <python:type-aliases>`. A description of
the type alias, such as the docstring can be, placed in the body of
the directive.

.. versionadded:: 7.3

.. rubric:: options

.. rst:directive:option:: type: the type that the alias represents
:type: text
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.. rst:directive:: .. py:type:: name
Describes a :ref:`type alias <python:type-aliases>`. A description of
the type alias, such as the docstring can be, placed in the body of
the directive.
.. versionadded:: 7.3
.. rubric:: options
.. rst:directive:option:: type: the type that the alias represents
:type: text
.. rst:directive:: .. py:type:: name
Describe a :ref:`type alias <python:type-aliases>`.
This directive supports an optional description body, e.g.::
.. code-block:: rst
.. py:type:: UInt64
Represent a 64-bit positive integer.
.. rubric:: options
.. rst:directive:option:: canonical
:type: text
The canonical type represented by this alias, e.g.::
.. code-block:: rst
.. py:type:: StrPattern
:canonical: str | re.Pattern[str]
Represent a regular expression or a compiled pattern.
.. versionadded:: 7.3

I suggested canonical because type is a bit weird in the sense it's not the type of the type but rather the underlying type. In addition, I am wondering whether the aliased type should be the fully-qualified name (in which case we wouldn't have any issues with references I think and we could have "clickable" items) or if it's simply a text (I'll probably comment below).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the type aliases be considered as clickable items in base classes / type annotations, etc and so? (i.e., do they behave similarly to a class?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they would be.

@AWhetter AWhetter force-pushed the fix_7896 branch 4 times, most recently from 042ed21 to 9579c8a Compare April 23, 2024 03:11
@AA-Turner AA-Turner merged commit e38a60d into sphinx-doc:master Jul 11, 2024
@AA-Turner
Copy link
Member

Thanks @AWhetter!

A

@AA-Turner AA-Turner added this to the 7.4.0 milestone Jul 13, 2024
@AWhetter AWhetter deleted the fix_7896 branch July 19, 2024 02:55
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

introduce directive for type alias

5 participants